import {EasyACtxHandle} from "./EasyACtxHandle";
import {EasyACtx} from "./EasyACtx";

import {TsReflect} from "../util/TsReflect";
import {Controller} from "./Controller";
import {_} from "../util/UtilTs";


/**
 * 拦截器
 *
 */

//拦截器类型
export enum InterceptorType{
    flow, //按顺序执行的（分情况，添加到action前后相应的位置）
    clear, //清除前面同类型的拦截器，消除型
}

export type InterceptorInsOrClass =Interceptor | typeof Interceptor; //可统一处理
export type InterceptorObjArray = Array<InterceptorInsOrClass>;


export class Interceptor{
    type = InterceptorType.flow;
    beforeHandle:EasyACtxHandle;//action前置处理器
    afterHandle:EasyACtxHandle;

    forceContinue =false;

    constructor(opts?:Partial<Interceptor>){
        const ins = this;
        if(opts){
            _.merge(this,opts);
        }
        Interceptor.createHandle(ins,'before');
        Interceptor.createHandle(ins,'after');
    }

    before(ctx:EasyACtx){

    }

    after(ctx:EasyACtx){

    }

    static createHandle(ins,funcName){
        const func = ins[funcName].bind(ins);
        //这才是重写的方法
        if(TsReflect.hasInProto(ins,funcName)){
            const handle = new EasyACtxHandle(func);
            handle.forceContinue = ins.forceContinue;
            handle.from = ins;
            ins[`${funcName}Handle`] = handle;
        }
    }


    //如果是实例，返回 实例本身，否则创建
    static tryCreateInter(obj:InterceptorInsOrClass, type:InterceptorType):Interceptor{
        let inter = obj as Interceptor;
        if(!(obj instanceof Interceptor)){
            inter = TsReflect.createInstance<Interceptor>(obj);
            inter.type =type;
        }
        return inter;
    }

    //如果不是 实例(而是类)，则创建示例
    static createIntersIfNotIns(inters:InterceptorObjArray, type:InterceptorType){
        const ret:Array<Interceptor> = [];
        inters.forEach((obj)=>{
            ret.push(Interceptor.tryCreateInter(obj,type));
        });
        return ret;
    }

    static AddInterceptorToCollection = (array:InterceptorObjArray, clazz:InterceptorInsOrClass, interType:InterceptorType)=>array.push(Interceptor.tryCreateInter(clazz,interType));

    static getHandlesFromFlowInterceptors(inters){
        const ret = {
            beforeHandles:[],
            afterHandles:[],
        };
        inters = _.filter(inters, _.negate(_.isNil));

        _.each(inters, (inter) => {
                inter.beforeHandle && ret.beforeHandles.push(inter.beforeHandle);
                inter.afterHandle && ret.afterHandles.push(inter.afterHandle);
        });
        return ret;
    }

}





//key 存在，说明是方法级别，否则，是类级别
function addIntersByTargetType(inters:Array<Interceptor>,interType:InterceptorType,target,key?){

        if(_.isNil(key)){  //类级拦截器
            // * 为什么要反着连接呢~~因为 ts 里注解的执行顺序是从下到上的
            // * https://www.typescriptlang.org/docs/handbook/decorators.html
            // * 而且 方法级别的先执行，类级别的后执行
            target.__init((ins:Controller)=>{
                ins.interceptors = inters.concat(ins.interceptors);
            });
            return target;
        }
        else { //方法级别拦截器

            if(Controller.isControllerDClazz(target)){
                const clazzFunc = target[key];
                const clazz = target.constructor;
                clazz.__init((ins)=>{
                    const acName = clazzFunc.name;
                    const oldInters = _.get(ins,`tempActionProps.${acName}.interceptors`,[]);
                    _.set(ins, `tempActionProps.${acName}.interceptors`, inters.concat(oldInters)); //同样需要反着连接
                });
            }
            else {
                throw "拦截器注解只能放在类（继承自controller） 或者方法上"
            }
        }
}

function base (interType:InterceptorType,interceptors:InterceptorObjArray){
    const inters:Array<Interceptor> = Interceptor.createIntersIfNotIns(interceptors,interType);
    return function(target, key?) {
        addIntersByTargetType(inters,interType,target,key)
    }
}

export const intercept = (...interceptors:InterceptorObjArray)=> base(InterceptorType.flow, interceptors);

export const clearInterceptor = (...interceptors:InterceptorObjArray)=> base(InterceptorType.clear, interceptors);





/*
export const after = (...interceptors:InterceptorObjArray)=> base(PlaceType.after,InterceptorType.flow, interceptors);
export const clearAfter= (...interceptors:InterceptorObjArray)=> base(PlaceType.after,InterceptorType.clearBefore, interceptors);



*/